﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Web;
using System.Web.Caching;

namespace Luminji.Core.Web.Safe
{
    public class BruteForceDefender
    {
        private static int _blockSenconds;
        private static int _blockTimes;

        /// <summary>
        /// the same user can't login * times in * seconds
        /// </summary>
        /// <param name="seconds"></param>
        /// <param name="times"></param>
        /// <param name="loginId"></param>
        public static void Defend(int seconds, int times, string loginId)
        {
            _blockSenconds = seconds;
            _blockTimes = times;
            CountTimeCache cache = CountTimeCache.GetCache(loginId);
            cache.AddCount();
            if (cache.Count > 1)
            {
                if ((DateTime.Now - cache.FirstTime).TotalSeconds > _blockSenconds)
                {
                    cache.InitCountAndFirstTime();
                }
                else
                {
                    if (cache.Count > _blockTimes)
                    {
                        throw new BruteForceException();
                    }
                }
            }
        }

        [Serializable]
        public class BruteForceException : Exception
        {
            //
            // For guidelines regarding the creation of new exception types, see
            //    http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpgenref/html/cpconerrorraisinghandlingguidelines.asp
            // and
            //    http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dncscol/html/csharp07192001.asp
            //

            public BruteForceException()
            {
            }

            public BruteForceException(string message)
                : base(message)
            {
            }

            public BruteForceException(string message, Exception inner)
                : base(message, inner)
            {
            }

            protected BruteForceException(
                SerializationInfo info,
                StreamingContext context)
                : base(info, context)
            {
            }
        }
        private class CountTimeCache
        {
            public string Key { get; set; }
            public int Count { get; set; }
            public DateTime FirstTime { get; set; }

            public static CountTimeCache GetCache(string longId)
            {
                CountTimeCache cache = HttpContext.Current.Cache.Get(longId) as CountTimeCache
                    ?? new CountTimeCache()
                           {
                               Key = longId,
                               Count = 0,
                               FirstTime = DateTime.Now

                           };
                return cache;
            }

            public void AddCount()
            {
                this.Count++;
                SetCache(this);
            }

            public void InitCountAndFirstTime()
            {
                this.FirstTime = DateTime.Now;
                Count = 1;
                SetCache(this);
            }

            private void SetCache(CountTimeCache countTimeCache)
            {
                HttpContext.Current.Cache.Insert(countTimeCache.Key, countTimeCache, null,
                    Cache.NoAbsoluteExpiration, TimeSpan.FromSeconds(_blockSenconds),
                    CacheItemPriority.NotRemovable, null);
            }
        }

    }
}
